package main

import (
	"context"
	"hedge/persistence"
	"sync"
	"time"

	"gitee.com/hacker61/go-binance/v2"
	"gitee.com/hacker61/go-binance/v2/futures"
)

var (
	binanceClient *binance.Client
	futuresClient *futures.Client
	ppersistence  persistence.Persistence
)

var (
	pArbitrageObj *SymbolArbitrage
)

func main_init() {
	binanceClient = binance.NewClient(conf.ApiKey, conf.SecretKey)
	futuresClient = futures.NewClient(conf.ApiKey, conf.SecretKey)

	pArbitrageObj = nil

	if ppersistence == nil {
		ppersistence = persistence.PersistenceMysql{}.Init(conf.MysqlOpt.Host, conf.MysqlOpt.Port, conf.MysqlOpt.User, conf.MysqlOpt.Passwd, conf.MysqlOpt.DBName)
	}
}

func init() {
	conf_init()
	order_init()
	logger_init()
	main_init()
}

func main() {
	wg := sync.WaitGroup{}

	binance.WebsocketKeepalive = true
	var doneC, fdoneC, doneC2, fdoneC2, doneC3 chan struct{}
	var err, ferr error
	var binanceWsBookTickerServe, futuresWsBookTickerServe, binanceWsUserDataServ, futuresWsUserDataServ func()
	binanceWsBookTickerServe = func() {
		doneC, _, err = binance.WsBookTickerServe(conf.Symbol, func(event *binance.WsBookTickerEvent) {
			if pArbitrageObj != nil {
				pArbitrageObj.binanceBookTickerC <- event
			}
		}, func(e error) {
			Error.Println(e)
			if doneC != nil {
				doneC <- struct{}{}
			}
			if binanceWsBookTickerServe != nil {
				binanceWsBookTickerServe()
			}
		})
		if err != nil {
			Error.Printf("binance WsBookTickerServe error: %v\n", err)
			panic(err)
		}
	}
	futuresWsBookTickerServe = func() {
		fdoneC, _, ferr = futures.WsBookTickerServe(conf.Symbol, func(event *futures.WsBookTickerEvent) {
			if pArbitrageObj != nil {
				pArbitrageObj.futuresBookTickerC <- event
			}
		}, func(e error) {
			Error.Println(e)
			if fdoneC != nil {
				fdoneC <- struct{}{}
			}
			if futuresWsBookTickerServe != nil {
				futuresWsBookTickerServe()
			}
		})
		if ferr != nil {
			Error.Printf("futures WsBookTickerServe error: %v\n", ferr)
			panic(ferr)
		}
	}
	binanceWsBookTickerServe()
	futuresWsBookTickerServe()

	listenerKey, err := binanceClient.NewStartUserStreamService().Do(context.Background())
	if err != nil {
		Error.Println(err)
		panic(err)
	}

	flistenerKey, ferr := futuresClient.NewStartUserStreamService().Do(context.Background())
	if ferr != nil {
		Error.Println(ferr)
		panic(ferr)
	}

	defer func() {
		err = binanceClient.NewCloseUserStreamService().ListenKey(listenerKey).Do(context.Background())
		ferr = futuresClient.NewCloseUserStreamService().ListenKey(flistenerKey).Do(context.Background())
		if err != nil {
			Error.Println(err)
		}
		if ferr != nil {
			Error.Println(ferr)
		}
	}()

	ticker := time.NewTicker(30 * time.Minute)
	doneC3 = make(chan struct{}, 1)
	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			select {
			case <-ticker.C:
				err = binanceClient.NewKeepaliveUserStreamService().ListenKey(listenerKey).Do(context.Background())
				if err != nil {
					Error.Println(err)
					panic(err)
				}
				ferr = futuresClient.NewKeepaliveUserStreamService().ListenKey(flistenerKey).Do(context.Background())
				if ferr != nil {
					Error.Println(ferr)
					panic(ferr)
				}
			case <-doneC3:
				return
			}
		}
	}()

	binanceWsUserDataServ = func() {
		doneC2, _, err = binance.WsExecutionReportServe(listenerKey, func(event *binance.WsExecutionReportEvent) {
			if event.OrderStatus == binance.OrderStatusTypeFilled && event.Symbol == conf.Symbol && pArbitrageObj != nil {
				Info.Println(*event)
				select {
				case <-pArbitrageObj.chanBinanceFilledOrder:
					pArbitrageObj.chanBinanceFilledOrder <- *event
				default:
					pArbitrageObj.chanBinanceFilledOrder <- *event
				}
			}
		}, func(e error) {
			Error.Println(e)
			if doneC2 != nil {
				doneC2 <- struct{}{}
			}
			if binanceWsUserDataServ != nil {
				binanceWsUserDataServ()
			}
		})
		if err != nil {
			Error.Printf("binance WsExecutionReportServe error: %v\n", err)
			panic(err)
		}
	}

	futuresWsUserDataServ = func() {
		fdoneC2, _, ferr = futures.WsUserDataServe(flistenerKey, func(event *futures.WsUserDataEvent) {
			if event.Event != futures.UserDataEventTypeOrderTradeUpdate {
				return
			}
			orderTradeUpdate := event.OrderTradeUpdate
			if orderTradeUpdate.Symbol != conf.Symbol {
				return
			}
			Info.Println(orderTradeUpdate)
			if orderTradeUpdate.Status == futures.OrderStatusTypeFilled && pArbitrageObj != nil {
				select {
				case <-pArbitrageObj.chanFuturesOrderTradeUpdate:
					pArbitrageObj.chanFuturesOrderTradeUpdate <- orderTradeUpdate
				default:
					pArbitrageObj.chanFuturesOrderTradeUpdate <- orderTradeUpdate
				}
			}
		}, func(e error) {
			Error.Println(e)
			if fdoneC2 != nil {
				fdoneC2 <- struct{}{}
			}
			if futuresWsUserDataServ != nil {
				futuresWsUserDataServ()
			}
		})
		if ferr != nil {
			Error.Printf("futures WsUserDataServe error: %v\n", ferr)
			panic(ferr)
		}
	}

	binanceWsUserDataServ()
	futuresWsUserDataServ()

	for i := 0; i < conf.CreateCount; i++ {
		pArbitrageObj = NewSymbolArbitrage(conf.Symbol, binanceClient, futuresClient)
		pArbitrageObj.StartRuntine()
	}

	doneC <- struct{}{}
	fdoneC <- struct{}{}
	doneC2 <- struct{}{}
	fdoneC2 <- struct{}{}
	doneC3 <- struct{}{}

	Info.Println("Run Over")

	wg.Wait()
}
